﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Sonic.Framework.Enums;
using Sonic.Framework.Interfaces;
using System.Xml;

namespace Sonic.Framework.ProcessorToolDirectives.Default
{
    /// <summary>
    /// Date stamp files
    /// </summary>
    public class Concatenator : IPluginBase, IProcessorToolDirective
    {
        #region Interface implementation

        private const string _name = "Concatenator";
        public string Name
        {
            get { return _name; }
        }

        private Dictionary<string, string> _attributes;
        public Dictionary<string, string> Attributes
        {
            get
            {
                return _attributes;
            }
        }

        #endregion Interface implementation

        private enum ConcatenationScrope
        {
            File,
            Directory
        };

        private FileInfo Output
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        public Concatenator()
        {
            this._attributes = new Dictionary<string, string>();
        }

        public bool Execute(ISonicProcessor processor, ISiteDirectory siteDir, ref List<FileInfo> directoryFileInfos, XmlDocument argumentsXml)
        {
            // Get the output path
            string relativeOutputPath = GetDirectiveCallAttributeFromArgXml(argumentsXml, "output");
            if (relativeOutputPath == null || relativeOutputPath.Trim() == "")
            {
                throw new ArgumentException("Concatenator must have a valid output file path");
            }

            // Get any optional params
            bool deleteSourceFiles = false;
            try
            {
                deleteSourceFiles = bool.Parse(GetDirectiveCallAttributeFromArgXml(argumentsXml, "deleteSourceFiles"));
            }
            catch
            {
                deleteSourceFiles = false;
            }


            // Track the directory to output to
            DirectoryInfo outputDirectory = siteDir.DirectoryInfo;

            // Referene where the file should be written to
            FileInfo output = new FileInfo( RelativizePath(outputDirectory.FullName, relativeOutputPath) );

            // Load details about paths to concatenate
            XmlNodeList paths = argumentsXml.SelectNodes("/Arguments/Directive/Path");
            List<FileInfo> filesToConcatenate = new List<FileInfo>();
            foreach (XmlNode path in paths)
            {
                string scope = "";
                try
                {
                    scope = path.Attributes["scope"].Value;
                }
                catch
                {
                    // Scope was not present, assume file
                    scope = "File";
                }

                ConcatenationScrope concatenationScope;
                try
                {
                    concatenationScope = (ConcatenationScrope)Enum.Parse(typeof(ConcatenationScrope), scope, true);
                }
                catch
                {
                    // Scope could not be determined, fail
                    continue;
                }

                string relativeFilePath = path.InnerText;

                switch (concatenationScope)
                {
                    case ConcatenationScrope.File:
                        FileInfo file = new FileInfo( RelativizePath(outputDirectory.FullName, relativeFilePath) );
                        filesToConcatenate.Add(file);
                        break;

                    case ConcatenationScrope.Directory:
                        DirectoryInfo dir = new DirectoryInfo(RelativizePath(outputDirectory.FullName, relativeFilePath));
                        try
                        {
                            filesToConcatenate.AddRange(dir.GetFiles());
                        }
                        catch (DirectoryNotFoundException)
                        {
                            
                        }
                        catch
                        {

                        }
                        break;
                }
            }

            Concatenate(processor, output, filesToConcatenate, deleteSourceFiles);

            return true;
        }

        /// <summary>
        /// Concatenates the passed in files list into the output file, optionally deleting the input files as it goes
        /// </summary>
        /// <param name="output"></param>
        /// <param name="filesToConcatenate"></param>
        /// <param name="deleteSourceFiles"></param>
        /// <returns></returns>
        protected FileInfo Concatenate(ISonicProcessor processor, FileInfo output, List<FileInfo> filesToConcatenate, bool deleteSourceFiles)
        {
            StreamWriter sw = null;

            try
            {
                sw = new StreamWriter(output.FullName, false, UTF8Encoding.UTF8);

                foreach (FileInfo file in filesToConcatenate)
                {
                    StreamReader sr = null;
                    try
                    {
                        sr = new StreamReader(file.FullName);
                        sw.Write(sr.ReadToEnd());
                        sw.WriteLine();
                    }
                    catch (FileNotFoundException)
                    {
                        // File not found
                        // TODO: log
                    }
                    catch (Exception)
                    {
                        // General exception
                        // TODO: log
                    }
                    finally
                    {
                        if (sr != null)
                        {
                            sr.Close();
                        }
                    }

                    if (deleteSourceFiles)
                    {
                        file.Delete();
                    }
                }
            }
            catch (DirectoryNotFoundException)
            {
                // Directory not found
                // TODO: Log
            }
            catch (Exception)
            {
                // Log exception
                // TODO: Log
            }
            finally
            {
                if (sw != null)
                {
                    sw.Close();
                }
            }

            // Track updates to file names that will need to be changed in content files
            FileReferenceUpdate concatFileRefUpdate = new FileReferenceUpdate();
            concatFileRefUpdate.FileReferenceUpdateType = FileReferenceUpdateType.Combined;
            concatFileRefUpdate.To = output;
            concatFileRefUpdate.From = filesToConcatenate;
            processor.AddFileReferenceUpdate(concatFileRefUpdate);

            return output;
        }

    }
}
